package com.eol.web.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.eol.entity.po.log.ZInterfaceLog;
import com.eol.service.log.ZInterfaceLogService;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.encoder.org.apache.commons.lang.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.eol.common.annotation.HttpApi;
import com.eol.common.base.BaseResponse;
import com.eol.common.base.BaseResponseCode;
import com.eol.common.constants.BaseCommonConstants;
import com.eol.common.context.BaseContextHandler;
import com.eol.common.exception.BaseException;
import com.eol.common.jwt.JwtUtils;
import com.eol.common.jwt.TokenData;
import com.eol.common.sequence.Sequence;
import com.eol.common.utils.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Objects;

/**
 * Created with IntelliJ IDEA.
 * User: liulinhui
 * Date: 2019/10/30
 * Time: 7:14 PM
 * Description: HttpInteceptor
 */
@Aspect
@Configuration
@Order(-1)
@Slf4j
public class HttpInterceptor {

    @Resource
    private Sequence sequence;
    @Resource
    private JwtUtils jwtUtils;
    @Resource
    private ZInterfaceLogService zInterfaceLogService;

    //切面
    @Pointcut("@annotation(com.eol.common.annotation.HttpApi)")
    public void allMethod() {
    }

    @Around("allMethod()")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        long beginTime = System.currentTimeMillis();
        long uuid = sequence.nextId();  //流水号
        TokenData tokenData;
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
        HttpApi httpApi = method.getAnnotation(HttpApi.class);
        BaseResponse apiRes; //HTTP接口返回值
        Object res;
        Long _t = null; //token剩余时间
        String token;
        try {
            String _v = request.getHeader(BaseCommonConstants.APP_VERSION);//客户端版本
            token = request.getParameter(BaseCommonConstants.ACCESS_TOKEN);   //tokan
            if (null == token)
                token = request.getHeader(BaseCommonConstants.ACCESS_TOKEN);
            if (httpApi.login() && !StringUtils.isNotNullOrEmpty(token)) {
                throw new BaseException(BaseResponseCode.TOKEN_INVALID);
            }
            if (StringUtils.isNotBlank(token) && !httpApi.refreshToken()) {
                tokenData = jwtUtils.parseToken(token);
                BaseContextHandler.setAccessToken(token);
                BaseContextHandler.setTokenData(tokenData);
                _t = jwtUtils.getTokenLeftTime(tokenData);
            }
            res = joinPoint.proceed();
            if (res instanceof BaseResponse) {
                apiRes = (BaseResponse) res;
            } else {
                apiRes = new BaseResponse(res);
            }
        } catch (Throwable throwable) {
            log.error(ExceptionUtils.getFullStackTrace(throwable));
            if (throwable instanceof BaseException) {
                apiRes = BaseResponse.error((BaseException) throwable);
            } else {
                apiRes = BaseResponse.error("系统繁忙!");
            }
        }
        apiRes.setUuid(uuid);
        if (null != _t) {
            apiRes.set_t(_t);
        }
        if (httpApi.log()) {
            StringBuilder params = new StringBuilder("{");
            //参数值
            Object[] argValues = joinPoint.getArgs();
            //参数名称
            String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            if (argValues != null) {
                for (int i = 0; i < argValues.length; i++) {
                    String input = "";
                    try {
                        input = JSON.toJSONString(argValues[i]);
                    } catch (Exception e) {
                        continue;
                    }
                    params.append(" ").append(argNames[i]).append(": ").append(input);
                }
            }
            params.append("}");
            String ip = StringUtils.getIp(request);
            ZInterfaceLog zdInterfaceLog = ZInterfaceLog
                    .builder()
                    .uuid(uuid)
                    .param(params.toString())
                    .clientIp(StringUtils.getIp(request))
                    .result(JSONObject.toJSONString(apiRes))
                    .location(StringUtils.getCityInfo(ip))
                    .status(apiRes.getCode())
                    .resourceName(httpApi.name())
                    .url(request.getServletPath())
                    .time(new Date())
                    .consumeTime((int) (System.currentTimeMillis() - beginTime))
                    .build();
            if (StringUtils.isNotBlank(BaseContextHandler.getAccessToken()) && null != BaseContextHandler.getTokenData()) {
                zdInterfaceLog.setUserId(BaseContextHandler.getTokenData().getUserId());
                zdInterfaceLog.setPhoneNum(BaseContextHandler.getTokenData().getPhoneNum());
            }
            zInterfaceLogService.insertLog(zdInterfaceLog);
        }
        if (BaseResponseCode.OK.getCode() != apiRes.getCode() && null != response) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
        return apiRes;
    }

    @After("allMethod()")
    public void afterMethod() {
        BaseContextHandler.remove();
    }
}
